"""Pylons environment configuration"""
import os
from StringIO import StringIO
import traceback

import pkg_resources

from pylons import config
from sqlalchemy import engine_from_config

import mtp.lib.app_globals as app_globals
import mtp.lib.helpers
from mtp.config.routing import make_map
from mtp.lib.mtpparser import mtpparse
from mtp.lib.configobj import ConfigObj
from mtp.lib.validate import Validator as ConfigObjValidator
from mtp.lib.util import DefaultDict
from mtp.lib.pageaction import PageAction
from mtp.lib import helpers as h

import logging
log = logging.getLogger(__name__)

config_spec = ConfigObj(StringIO("""
[DEFAULT]
here = string(default="%s")

[wiki]
title = string(default="MTP Wiki")
datetime_format = string(default="%%c")

[auth]
plugin = string(default="local_password")
admin_user = string(default="admin")
admin_password = string(default="admin")
admin_host = string(default="127.0.0.1")

[plugin:attachments]
enabled = boolean(default=True)
load_position = integer(default=20)

[plugin:history]
enabled = boolean(default=True)
load_position = integer(default=10)

[plugin:configerator]
enabled = boolean(default=False)
load_position = integer(default=0)

[plugin:comments]
enabled = boolean(default=True)
load_position = integer(default=15)

[plugin:permissionator]
enabled = boolean(default=True)

[plugin:rss]
enabled = boolean(default=True)

[plugin:tags]
enabled = boolean(default=True)
load_position = integer(default=0)

[plugin:pagetags]
enabled = boolean(default=True)
load_position = integer(default=17)

[plugin:wikistructure]
enabled = boolean(default=True)
load_position = integer(default=16)

[plugin:usermgr]
enabled = boolean(default=True)

[macro:list-macros]
enabled = boolean(default=True)

[macro:list-pages]
enabled = boolean(default=True)

[macro:toc]
enabled = boolean(default=True)
""" % os.getcwd()))

default_plugin_spec = """
[plugin:%(name)s]
enabled = boolean(default=False)
"""

def find_from_eps(epname):
    ret = {}
    log.info('looking for entry points in %s...' % epname)
    for ep in pkg_resources.iter_entry_points(epname):
        ret[ep.name] = ep
        log.info('    found %s = %s:%s' % (ep.name, ep.module_name, ep.attrs))
    return ret

def find_macros():
    return find_from_eps('mtp.macros')

def find_plugins():
    return find_from_eps('mtp.plugins')

def find_auth_plugin(name):
    auth_plugins = find_from_eps('mtp.auth_plugins')
    return auth_plugins[name]


def configure_mtp():
    global config_spec
    #if config.has_key('routes.middleware'):
    #    oldmapper = config['routes.map']

    config['mtp.map'] = config['routes.map'] = make_map()

    #if config.has_key('routes.middleware'):
    #    # mess with the mapper a bit 
    #    # XXX this might be highly dangerous... XXX
    #    config['routes.middleware'].mapper = config['routes.map']
    #    config['routes.map'].req_data = oldmapper.req_data

    # function for transforming wiki content into xhtml
    config['mtp.formatter'] = mtpparse

    def loadConfig(configspecc):
        # will probably use the paster ini file later, now its safer
        # to have a seperate config
        mtpconf = config['mtp.config'] = ConfigObj(config['mtp.configfile'], 
                                                    configspec=configspecc)
        val = ConfigObjValidator()
        if not mtpconf.validate(val):
            raise "config error"   
        return mtpconf

    # lists of all macros/plugins and their entry point
    # (NB they are not loaded)
    config['mtp.all_macros'] = find_macros()
    config['mtp.all_plugins'] = find_plugins()

    # add some sane defaults to the configspec for all plugins
    default_spec = []
    for plugin_name in config['mtp.all_plugins']:
        default_spec.append( default_plugin_spec % {'name': plugin_name} )
    default_configobj = ConfigObj(StringIO("\n".join(default_spec)))
    default_configobj.merge(config_spec)
    config_spec = default_configobj
    
    mtpconf = loadConfig(config_spec)

    config['mtp.title'] = mtpconf['wiki']['title']
    log.info('title = %s' % config['mtp.title'])

    # dict of controllers provided 
    from mtp.controllers import pages, css, config as configcont
    config['mtp.controllers'] = {
        'pages': pages.PagesController,
        'css': css.CssController,
        'config': configcont.ConfigController,
    }

    # list of functions that will modify the wiki output somehow
    # functions take one arg, an xml element from lxml.etree
    config['mtp.plugin_wikimodifiers'] = []

    # dict of (controller, action):list of functions that return page action links
    # the special key None is used to indicate page actions that are displayed
    # on every page
    config['mtp.page_actions'] = DefaultDict(list)

    config['mtp.nav_items'] = []
    config['mtp.nav_items'].extend([
        PageAction('Index', h.url_for('home')),
        PageAction('Admin', h.url_for('config'), permission=('config', 'show')),
    ])

    # dict of file extension: list of formatters
    config['mtp.plugin_formatters'] = DefaultDict(list)
    from mtp.lib.formatter import RawFormatter, XMLFormatter
    config['mtp.plugin_formatters']['txt'].append(RawFormatter())
    config['mtp.plugin_formatters']['xml'].append(XMLFormatter())

    # dict of thing:set of actions of all known permissions
    config['mtp.permissions'] = perms = DefaultDict(set)
    perms['page'].update(('show', 'list', 'view', 'edit', 'create', 'delete'))

    # list of extra css templates to append to the main template
    config['mtp.extra_css'] = []

    # dict of (controller, action):list of iterables for items inserted
    # into the head section of the html output
    config['mtp.content.head'] = DefaultDict(list)

    # dict of (controller, action): list of iterables for items to be inserted
    # at the beggining/middle/end of pages ( <mutter>probably just for html</mutter> )
    # inserting content may not be available on all pages
    config['mtp.content.prepend'] = DefaultDict(list)
    config['mtp.content.append'] = DefaultDict(list)

    config['mtp.edit_page.content.prepend'] = []
    config['mtp.edit_page.content.insert'] = []
    config['mtp.edit_page.content.append'] = []
    config['mtp.edit_page.processors'] = []

    config['mtp.auth_plugin'] = find_auth_plugin(mtpconf['auth']['plugin']).load()()
    log.info('auth plugin = %s' % config['mtp.auth_plugin'])

    config['mtp.page.global_etag'] = 0
    config['mtp.page.etags'] = {}

    config['mtp.config_links'] = {}

    # plugins wanting to add macro's must add macro functions to
    # config['mtp.all_macros']
    # plugins are only loaded here not initialized to allow collecting of the 
    # config_spec
    config['mtp.plugins'] = plugins = {}
    plugin_config_spec = []
    for name, p in config['mtp.all_plugins'].iteritems():
        if (mtpconf.has_key('plugin:%s' % name) and
            mtpconf['plugin:%s' % name].get('enabled')):

            try:
                plugins[name] = plugin = p.load()
            except Exception:
                log.warn('loading plugin %s failed!' % name)
                log.warn(traceback.format_exc())
                continue

            if hasattr(plugin, 'configspec'):
                plugin_spec = ConfigObj(StringIO(plugin.configspec))
                config_spec.merge(plugin_spec)
    log.info('enabled plugins %s' % str(config['mtp.plugins'].keys()))

    #plugin_configobj = ConfigObj(StringIO("\n".join(plugin_config_spec)))
    #config_spec.merge(plugin_configobj)

    config_spec.write(open('spec.ini', 'w'))

    # reparse config with plugins config_spec
    log.debug('revalidating config_spec with plugin configspec')
    mtpconf = loadConfig(config_spec)

    # order the plugins 
    plugins = config['mtp.plugins'].items()
    def plugin_sorter(a, b):
        return cmp(mtpconf['plugin:%s' % a[0]].get('load_position', 1000000),
                   mtpconf['plugin:%s' % b[0]].get('load_position', 1000000))
    plugins.sort(cmp=plugin_sorter)

    log.debug('plugin load order %s' % str([p[0] for p in plugins]))

    # now config_spec has been properly setup actually init the plugins
    for name, plugin in plugins:
        config['mtp.plugins'][name] = plugin(**mtpconf['plugin:%s' % name])

    log.debug('controllers: %s' % str(config['mtp.controllers']))

    # now *all* available macros should be listed ( even those created by 
    # plugins ) *and* the config_spec has been created for them
    config['mtp.macros'] = macros = {}
    for name, m in config['mtp.all_macros'].iteritems():
        if (mtpconf.has_key('macro:%s' % name) and 
            mtpconf['macro:%s' % name].get('enabled')):
            # m will either be an entry point(needs loading) or a function 
            try:
                macros[name] = m.load() if isinstance(m, pkg_resources.EntryPoint) else m
            except Exception:
                log.warn('loading macro %s failed!' % name)
                log.warn(traceback.format_exc())
                continue
    log.info('enabled macros %s' % str(config['mtp.macros'].keys()))


def load_environment(global_conf, app_conf):
    """Configure the Pylons environment via the ``pylons.config``
    object
    """
    # Pylons paths
    root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    paths = dict(root=root,
                 controllers=os.path.join(root, 'controllers'),
                 static_files=os.path.join(root, 'public'),
                 templates=[os.path.join(root, 'templates')])

    # Initialize config with the basic options
    config.init_app(global_conf, app_conf, package='mtp',
                    template_engine='mako', paths=paths)

    config['pylons.g'] = app_globals.Globals()
    config['pylons.h'] = mtp.lib.helpers

    config['pylons.g'].sa_engine = engine_from_config(config,
                                                  'sqlalchemy.default.')




